package com.ykl.base.restful;

import com.ykl.utils.HttpDownloadHeaderUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

@Slf4j
public class GeneralCtrl {

    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    protected <R> ResponseEntity<Res<R>> validate(BindingResult valRes,String prefix, Supplier<R> onPass){
        return validate(valRes, ()->prefix, (bindRes)->-1,(th)->Pair.of(Integer.MIN_VALUE,th.getLocalizedMessage()), onPass);
    }

    protected <R> ResponseEntity<Res<R>> validate(BindingResult valRes, Supplier<String> errMsgPrefixSupp,
                                                  Function<BindingResult, Integer> invalidCodeGenerator,
                                                  Function<Throwable, Pair<Integer, String>> errorFormater,
                                                  Supplier<R> onPass){
        if(valRes.hasErrors()){
            StringBuffer msg = valRes.getAllErrors().stream().filter(Objects::nonNull)
                    .map(ObjectError::getDefaultMessage).collect(new Collector<String, StringBuffer, StringBuffer>() {
                        @Override
                        public Supplier<StringBuffer> supplier() {
                            return StringBuffer::new;
                        }

                        @Override
                        public BiConsumer<StringBuffer, String> accumulator() {
                            return (buf, err) -> buf.append(err).append(",");
                        }

                        @Override
                        public BinaryOperator<StringBuffer> combiner() {
                            return (b1,b2)->b1.append(b2);
                        }

                        @Override
                        public Function<StringBuffer, StringBuffer> finisher() {
                            return (buf) -> buf.insert(0, errMsgPrefixSupp.get()).deleteCharAt(buf.length()-1).append("。");
                        }

                        @Override
                        public Set<Characteristics> characteristics() {
                            return Collections.emptySet();
                        }
                    });
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Res.ofExcept(invalidCodeGenerator.apply(valRes), msg.toString()));
        }else{
            try{
                if(onPass!=null){
                    return ResponseEntity.of(Optional.of(Res.ofSeccess(onPass.get())));
                }else{
                    return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).body(null);
                }
            }catch (Throwable e){
                log.warn(e.getLocalizedMessage(), e);
                int code = Integer.MIN_VALUE;
                String msg = e.getMessage();
                if(errorFormater!=null){
                    Pair<Integer, String> p = errorFormater.apply(e);
                    if(p!=null){
                        code=p.getLeft();
                        msg=p.getRight();
                    }
                }
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Res.ofExcept(code,msg));
            }
        }
    }

    /**
     * 写入下载文件.
     * @param req http请求.
     *            <p>用于写入头时读取请求字符集，避免文件名乱码.</p>
     * @param resp http应答.
     * @param charset 正文字符集.
     * @param filenameFac 文件名工厂.
     * @param contentFac 正文工厂.
     */
    protected void writeDownloadFile(HttpServletRequest req, HttpServletResponse resp, Charset charset, Supplier<String> filenameFac, Supplier<byte[]> contentFac){
        try(OutputStream out=resp.getOutputStream()){
            byte[] data = contentFac.get();
            HttpDownloadHeaderUtils.writeAttachmentHeaders(filenameFac.get(), req, resp);
            resp.setCharacterEncoding(charset.name());
            out.write(data);
            out.flush();
        }catch (Exception th){
            resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            log.warn(th.getLocalizedMessage(), th);
        }
    }

}
